package com.zkh.myutils.database.dbpool;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import com.zkh.myutils.database.DatabaseException;
import com.zkh.myutils.system.ConfigLoader;
import com.zkh.myutils.utils.StringUtils;

/**
 * 连接池对象
 * @author zkh
 */
@SuppressWarnings("unchecked")
public class ConnectConfigure {
	//数据库配置信息
	private static Map<String, DataBaseInfo> databaseInfo = new HashMap<>();
	//默认数据库
	private static String defualtSource;
	//池子初始大小(默认为5)
	private static int poolIniSize = 5;
	//池子最大容量(默认为20)
	private static int poolMaxSize = 20;
	///每次增加连接数量(默认为3)
	private static int poolStep = 3;
	//连接回收时间（秒）。如果连接池的连接个数大于初始个数，系统将定时按poolStep数量销毁，直到恢复到初始容量为止。
	//取值说明：小于0时，代表关闭连接回收功能（默认），[0, 30]时，默认30秒，其余取值按配置值计。
	private static int recoveryTime = -1;
	//是否显示池子日志
	private static boolean showPoolLog;
	//检查连接是否有效的时间间隔
	private static int pingInterval = 30;
	//是否显示SQL
	private static boolean showSql = false;
	
	//初始化
	static {
		//数据库配置
		Map<String, Object> database = ConfigLoader.getMap("database");
		/** 数据库属性 **/
		LinkedHashMap<String, Object> attrMap = (LinkedHashMap<String, Object>) database.get("attr");
		//有值
		if(attrMap!=null) {
			//是否打印输出池子日志到控制台
			showPoolLog = "true".equals(attrMap.get("showPoolLog"));
			//是否显示SQL
			showSql = "true".equals(attrMap.get("showSql"));
		}
		/** 数据源配置 **/
		//处理数据源配置
		Object dataSource = database.get("dataSource");
		//如果是Map，则默认为单数据源配置
		if(dataSource instanceof LinkedHashMap) {
			//类型转换并处理
			dealDatasource((LinkedHashMap<String, String>) dataSource);
		}//如果是List，则默认为多数据源配置
		else if(dataSource instanceof LinkedList) {
			//类型转换并遍历配置
			((LinkedList<LinkedHashMap<String, String>>) dataSource).forEach(ConnectConfigure::dealDatasource);
		}else {
			throw new DatabaseException("数据源未配置或配置错误");
		}
		/** 连接池配置 **/
		LinkedHashMap<String, Object> poolMap = (LinkedHashMap<String, Object>) database.get("poolAttr");
		//有值
		if(poolMap!=null) {
			//获取配置值
			poolIniSize = StringUtils.getNumberic((String) poolMap.get("poolIniSize"), poolIniSize).intValue();
			poolMaxSize = StringUtils.getNumberic((String) poolMap.get("poolMaxSize"), poolMaxSize).intValue();
			poolStep = StringUtils.getNumberic((String) poolMap.get("poolStep"),poolStep).intValue();
			recoveryTime = StringUtils.getNumberic((String) poolMap.get("recoveryTime"), recoveryTime).intValue();
			pingInterval = StringUtils.getNumberic((String) poolMap.get("pingInterval"), pingInterval).intValue();
			//过滤值
			recoveryTime = recoveryTime<0 ? -1 : (Math.max(recoveryTime, 30));
			pingInterval = Math.max(pingInterval, 30);
			//检查条件
			if(poolIniSize<1) {
				throw new DatabaseException("连接池初始大小至少为1");
			}
			if(poolIniSize>poolMaxSize) {
				throw new DatabaseException("连接池最大连接数不能小于连接池初始大小");
			}
		}
	}
	
	/**
	 * 处理数据源
	 * @param ds 数据源
	 */
	private static void dealDatasource(LinkedHashMap<String, String> ds) {
		//获取当前配置
		String id = ds.get("id"), type = ds.get("type"), def = ds.get("default"), driver = ds.get("driver"), url = ds.get("url");
		String username = ds.get("username"), password = ds.get("password");
		//检查配置
		if(StringUtils.isExistEmpty(id, driver, url, username, password)) {
			throw new DatabaseException("数据源配置缺失");
		}
		//数据库类型
		DBType dbType = DBType.getDbType(type);
		//连接属性
		DataBaseInfo info = new DataBaseInfo(driver, url, username, ConfigLoader.decodeEnc(password), dbType);
		//检查ID是否重复
		if(databaseInfo.containsKey(id)) {
			throw new DatabaseException("重复的数据源id："+id);
		}
		databaseInfo.put(id, info);
		//将第一个节点或指定节点设置为默认数据源
		if(defualtSource==null || "true".equals(def)) {
			defualtSource = id;
		}
	}
	
	/**
	 * 获取默认数据源
	 */
	public static DataBaseInfo getDefaultSourceConfig(){
		return getSourceConfigById(null);
	}
	
	/**
	 * 获取指定数据源
	 * @param id 数据源ID，为null时，使用默认数据源
	 */
	public static DataBaseInfo getSourceConfigById(String id){
		if(databaseInfo.isEmpty()) {
			throw new DatabaseException("未读取到数据库配置信息");
		}
		//如果ID为空
		if(id==null) {
			id = defualtSource;
		}
		//不存在
		if(id.isEmpty() || !databaseInfo.containsKey(id)) {
			throw new DatabaseException("指定的数据源不存在");
		}
		//返回配置
		return databaseInfo.get(id);
	}
	
	/**
	 * 是否显示SQL
	 */
	public static boolean isShowSql() {
		return showSql;
	}

	/**
	 * 获取连接池初始大小（如果使用连接池）
	 */
	public static int getPoolIniSize() {
		return poolIniSize;
	}
	
	/**
	 * 获取连接池最大连接数（如果使用连接池）
	 */
	public static int getPoolMaxSize() {
		return poolMaxSize;
	}
	
	/**
	 * 获取连接池每个增加连接个数（如果使用连接池）
	 */
	public static int getPoolStep() {
		return poolStep;
	}

	/**
	 * 获取默认数据源（即database.xml配置文件datasource标签的ID）
	 */
	public static String getDefualtSource() {
		return defualtSource;
	}
	
	/**
	 * 是否存在指定key的数据源
	 */
	public static boolean isKeyExist(String key) {
		return key!=null && databaseInfo.containsKey(key);
	}

	/**
	 * 是否打印输出池子日志到控制台
	 */
	public static boolean isShowPoolLog() {
		return showPoolLog;
	}

	/**
	 * 连接回收时间（秒）。如果连接池的连接个数大于初始个数，系统将定时按poolStep数量销毁，直到恢复到初始容量为止。<br>
	 * 取值说明：小于0时，代表关闭连接回收功能（默认），[0, 30]时，默认30秒，其余取值按配置值计。
	 */
	public static int getRecoveryTime() {
		return recoveryTime;
	}

	/**
	 * 获取ping间隔的时间（秒）。如果配置小于30，将默认为30。
	 */
	public static int getPingInterval() {
		return pingInterval;
	}
}
